<?php

declare(strict_types=1);

namespace App\Api\Service;

use App\Constants\OrderPayStatus;
use App\Constants\OrderStatus;
use App\Constants\SettingConfig;
use App\Exception\DefaultException;
use App\Model\Car;
use App\Model\Channel;
use App\Model\Insurance;
use App\Model\Lessee;
use App\Model\Member;
use App\Model\Order;
use App\Model\OrderCar;
use App\Model\OrderInsurance;
use App\Model\OrderLessee;
use App\Utils\WechatPay;
use Carbon\Carbon;
use Hyperf\Contract\LengthAwarePaginatorInterface;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\ModelNotFoundException;
use Hyperf\DbConnection\Db;
use Hyperf\HttpMessage\Exception\NotFoundHttpException;
use Throwable;

use function Hyperf\Config\config;

class OrderService
{
    /**
     * 订单确认
     * @param array $params
     * @return array
     */
    public function confirm(array $params): array
    {
        // 查询车辆
        $car = Car::query()
            ->where('id', $params['car_id'])
            ->firstOrFail();
        $CarService = new CarService();
        $car->price = $CarService->getCarPrice($car->toArray());
        $prices_calendar = $CarService->getCarPriceCalendar($car->toArray(), intval($params['start_at']), intval($params['end_at']));
        $car_price = '0';
        foreach ($prices_calendar as $v) {
            $car_price = bcadd((string)$v, $car_price);
        }
        // 查询承租人
        $lessees = Lessee::query()
            ->where('member_id', api_member_id())
            ->latest('is_default')
            ->get(['id', 'name', 'phone', 'id_card']);

        // 保险
        $insurances = Insurance::query()
            ->latest('level')
            ->where('car_id', $params['car_id'])
            ->get(['id', 'level', 'name', 'car_damage', 'car_damage_sub', 'tripartite', 'passenger', 'free_depreciation', 'no_need_advance', 'free_outage', 'original_price', 'price', 'detail']);
        if(empty($insurances) || count($insurances) < 1) {
            $insurances = Insurance::query()
                ->latest('level')
                ->where('car_id', 0)
                ->get(['id', 'level', 'name', 'car_damage', 'car_damage_sub', 'tripartite', 'passenger', 'free_depreciation', 'no_need_advance', 'free_outage', 'original_price', 'price', 'detail']);
        }
        return [
            'name' => $car->name,
            'cover' => $car->cover,
            'describe' => $car->describe,
            'price' => $car->price,
            'prices_calendar' => $prices_calendar,
            'car_price' => intval($car_price),
            'city' => $params['city'],
            'address' => $params['address'],
            'start_date' => Carbon::createFromTimestamp($params['start_at'])->format('Y年m月d日 H:i'),
            'end_date' => Carbon::createFromTimestamp($params['end_at'])->format('Y年m月d日 H:i'),
            'day' => ceil(($params['end_at'] - $params['start_at']) / 86400),
            'lessees' => $lessees,
            'insurances' => $insurances,
            'car_rental_must_read' => (string)ConfigService::get(SettingConfig::CAR_RENTAL_MUST_READ),
            'instructions_for_car_use' => (string)ConfigService::get(SettingConfig::INSTRUCTIONS_FOR_CAR_USE),
            'cleaning_fee' => 0, //ConfigService::cleaningFee(),
            'service_fee' => ConfigService::serviceFee(),
            'order_base_insure_fee' => ConfigService::orderBaseInsureFee(),
        ];
    }

    /**
     * 订单创建
     * @param array $params
     * @return array
     */
    public function store(array $params): array
    {
        // 查询车辆
        $car = Car::query()
            ->where('id', $params['car_id'])
            ->firstOrFail();
        $CarService = new CarService();
        $car->price = $CarService->getCarPrice($car->toArray());
        $prices_calendar = $CarService->getCarPriceCalendar($car->toArray(), intval($params['start_at']), intval($params['end_at']));
        $car_price = '0';
        foreach ($prices_calendar as $v) {
            $car_price = bcadd((string)$v, $car_price);
        }
        // 查询渠道
        $channelId = 0;
        if (!empty($params['channel_id'])) {
            $channelId = Channel::query()
                ->where('id', $params['channel_id'])
                ->value('id') ?? 0;
        }

        // 查询承租人
        $lessee = Lessee::query()
            ->where('id', $params['lessee_id'])
            ->where('member_id', api_member_id())
            ->firstOrFail();

        // 查询保险
        $insurance = [];
        if(isset($params['insurance_id'])){
            $insurance = Insurance::query()
                ->where('id', $params['insurance_id'])
                ->firstOrFail();
        }

        // 计算价格
        $day = ceil(($params['end_at'] - $params['start_at']) / 86400);
        $cleaningFee = ConfigService::cleaningFee();
        $serviceFee = ConfigService::serviceFee();
        $orderBaseInsureFee = ConfigService::orderBaseInsureFee();
        $totalPrice = 0;
        // 租金
        $totalPrice += $car_price;
        // 清洁费
        $totalPrice += $cleaningFee;
        // 服务费
        $totalPrice += $serviceFee;
        // 保险费
        if(!empty($insurance)) $totalPrice += bcmul((string)$day, (string)$insurance->price, 2);
        // 基础保险费
        $totalPrice += bcmul((string)$day, (string)$orderBaseInsureFee, 2);
        if (bccomp((string)$totalPrice, (string)$params['total_price']) !== 0) {
            throw new DefaultException('价格发生变动，请刷新重试');
        }

        Db::beginTransaction();
        try {
            // 创建订单
            $order = Order::create([
                'member_id' => api_member_id(),
                'car_id' => $car->id,
                'lessee_id' => $lessee->id,
                'channel_id' => $channelId,
                'no' => snowflake_id(),
                'city' => $params['city'],
                'address' => $params['address'],
                'start_at' => Carbon::createFromTimestamp($params['start_at']),
                'end_at' => Carbon::createFromTimestamp($params['end_at']),
                'day' => $day,
                'price' => $car->price,
                'cleaning_fee' => $cleaningFee,
                'service_fee' => $serviceFee,
                'insurance_price' => !empty($insurance) ? $insurance->price : 0,
                'total_price' => $totalPrice,
                'pay_lapse_at' => Carbon::now()->addMinutes(ConfigService::payLapseDate()),
            ]);

            // 创建订单快照车辆
            OrderCar::create([
                'order_id' => $order->id,
                'car_id' => $car->id,
                'car_category_ids' => $car->car_category_ids,
                'banner' => $car->banner,
                'cover' => $car->cover,
                'name' => $car->name,
                'price' => $car->price,
                'describe' => $car->describe,
                'base_config' => $car->base_config,
                'high_light_config' => $car->high_light_config,
            ]);

            // 创建订单快照乘租人
            OrderLessee::create([
                'order_id' => $order->id,
                'lessee_id' => $lessee->id,
                'member_id' => $lessee->member_id,
                'name' => $lessee->name,
                'phone' => $lessee->phone,
                'id_card' => $lessee->id_card,
                'is_default' => $lessee->is_default,
            ]);

            // 创建快照订单保险
            if(!empty($insurance)) OrderInsurance::create([
                'order_id' => $order->id,
                'insurance_id' => $insurance->id,
                'level' => $insurance->level,
                'name' => $insurance->name,
                'car_damage' => $insurance->car_damage,
                'car_damage_sub' => $insurance->car_damage_sub,
                'tripartite' => $insurance->tripartite,
                'passenger' => $insurance->passenger,
                'free_depreciation' => $insurance->free_depreciation,
                'no_need_advance' => $insurance->no_need_advance,
                'free_outage' => $insurance->free_outage,
                'original_price' => $insurance->original_price,
                'price' => $insurance->price,
                'detail' => $insurance->detail,
            ]);

            Db::commit();
        } catch (Throwable $e) {
            Db::rollBack();
            business_error_logger(__FUNCTION__, $e);
            throw new DefaultException('下单失败，请稍后重试');
        }

        return [
            'id' => $order->id,
        ];
    }

    /**
     * 获取支付参数
     * @param mixed $id
     * @return array
     */
    public function payParameter(mixed $id): array
    {
        // 查询用户
        $memberId = api_member_id();
        $member = Member::query()
            ->where('id', $memberId)
            ->firstOrFail(['openid']);

        // 当前时间
        $now = Carbon::now();

        // 查询订单
        $order = Order::query()
            ->where('id', $id)
            ->where('member_id', $memberId)
            ->where('pay_lapse_at', '>', $now)
            ->whereIn('pay_status', [OrderPayStatus::NONE, OrderPayStatus::ING])
            ->firstOrFail();

        // 微信支付
        $wechatPay = new WechatPay();

        Db::beginTransaction();
        try {
            // 更新用户支付单号
            $order->pay_no = snowflake_id();
            $order->pay_launch_at = $now;
            $order->pay_status = OrderPayStatus::ING;
            $order->save();

            // 预下单
            $prepayId = $wechatPay->prepay($member->openid, $order->pay_no, (string)$order->total_price);

            // 获取支付参数
            $parameters = $wechatPay->buildMiniAppConfig($prepayId);

            Db::commit();
        } catch (Throwable $e) {
            Db::rollBack();
            business_error_logger(__FUNCTION__, $e);
            throw new DefaultException('获取支付参数失败，请稍后重试');
        }

        return $parameters;
    }

    /**
     * 订单支付成功(测试使用)
     * @param mixed $id
     * @return void
     */
    public function paySuccess(mixed $id): void
    {
        // 线上环境禁止使用
        if (config('app_env') === 'prod') {
            throw new NotFoundHttpException();
        }

        // 查询用户
        $memberId = api_member_id();
        Member::query()
            ->where('id', $memberId)
            ->firstOrFail(['openid']);

        // 当前时间
        $now = Carbon::now();

        // 查询订单
        $order = Order::query()
            ->where('id', $id)
            ->where('member_id', $memberId)
            ->where('pay_lapse_at', '>', $now)
            ->where('pay_status', OrderPayStatus::NONE)
            ->firstOrFail();

        Db::beginTransaction();
        try {
            // 更新
            $order->pay_no = snowflake_id();
            $order->pay_launch_at = $now;
            $order->pay_confirm_at = $now;
            $order->pay_status = OrderPayStatus::SUCCESS;
            $order->status = OrderStatus::ING;
            $order->save();

            // 更新车辆
            Car::query()
                ->where('id', $order->car_id)
                ->update([
                    'volume' => Db::raw('`volume` + 1'),
                ]);

            Db::commit();
        } catch (Throwable $e) {
            Db::rollBack();
            business_error_logger(__FUNCTION__, $e);
        }
    }

    /**
     * 订单列表
     * @return LengthAwarePaginatorInterface
     */
    public function index(): LengthAwarePaginatorInterface
    {
        // 参数
        $request = request();
        $status = (int)$request->input('status', 0);
        $statusMapping = [
            1 => OrderStatus::WAIT_PAY,
            2 => OrderStatus::ING,
            3 => OrderStatus::COMPLETED,
        ];

        // 查询
        $res = Order::query()
            ->with(['orderCar:order_id,name,describe'])
            ->where('member_id', api_member_id())
            ->when(isset($statusMapping[$status]), function (Builder $builder) use ($statusMapping, $status) {
                $builder->where('status', $statusMapping[$status]);
            })
            ->oldest('status')
            ->latest('id')
            ->paginate();

        // 周
        $weeks = ['日', '一', '二', '三', '四', '五', '六'];

        // 组装数据
        /** @var Order $model */
        foreach ($res as $model) {
            $model->date = $model->created_at->format('Y.m.d H:i');
            $model->start_date = sprintf($model->start_at->format('Y年m月d日 周%\s H:i'), $weeks[$model->start_at->format('w')]);
            $model->end_date = sprintf($model->end_at->format('Y年m月d日 周%\s H:i'), $weeks[$model->end_at->format('w')]);
            $model->orderCar->setHidden(['order_id']);
            $model->setVisible(['id', 'date', 'status', 'orderCar', 'start_date', 'end_date', 'day', 'total_price']);
        }

        return $res;
    }

    /**
     * 订单详情
     * @param mixed $id
     * @return Order
     */
    public function show(mixed $id): Order
    {
        // 查询订单
        $model = Order::query()
            ->with(['orderCar:order_id,name,describe,cover', 'orderLessee:order_id,name,phone,id_card', 'orderInsurance:order_id,level,name,car_damage,car_damage_sub,tripartite,passenger'])
            ->where('id', $id)
            ->where('member_id', api_member_id())
            ->firstOrFail();

        // 组装数据
        $model->curren_time = time();
        $model->pay_lapse_time = $model->pay_lapse_at->timestamp;
        $model->start_date = $model->start_at->format('Y年m月d日 H:i');
        $model->end_date = $model->end_at->format('Y年m月d日 H:i');
        $model->date = $model->created_at->format('Y.m.d H:i');
        $model->pay_date = $model->getRawOriginal('pay_confirm_at') ? $model->pay_confirm_at->format('Y.m.d H:i') : '';
        $model->orderCar->setHidden(['order_id']);
        $model->orderLessee->setHidden(['order_id']);
        $model->orderInsurance->setHidden(['order_id']);
        $model->setVisible(['curren_time', 'pay_lapse_time', 'orderCar', 'city', 'address', 'start_date', 'end_date', 'day', 'price', 'cleaning_fee', 'service_fee', 'total_price', 'orderLessee', 'orderInsurance', 'no', 'date', 'pay_date', 'status']);

        return $model;
    }

    /**
     * 取消订单
     * @param mixed $id
     * @return void
     */
    public function updateCancel(mixed $id): void
    {
        // 查询
        $order = Order::query()
            ->where('id', $id)
            ->where('member_id', api_member_id())
            ->where('is_delivery', Order::DISABLE)
            ->where('status', OrderStatus::ING)
            ->firstOrFail();

        // 更新
        $order->cancelled_at = Carbon::now();
        $order->status = OrderStatus::CANCELLED;
        $order->save();
    }

    /**
     * 确认还车
     * @param mixed $id
     * @return void
     */
    public function updateReturn(mixed $id): void
    {
        // 查询
        $order = Order::query()
            ->where('id', $id)
            ->where('member_id', api_member_id())
            ->where('is_delivery', Order::ENABLE)
            ->where('status', OrderStatus::ING)
            ->firstOrFail();

        // 更新
        $order->return_at = Carbon::now();
        $order->status = OrderStatus::COMPLETED;
        $order->save();
    }
}
